import {computed, reactive, watch} from "@vue/composition-api";
import {Grid, Minimap} from "@/g6/g6";
import {flatten, suffixSize} from "@/utils/utils";
import {Command, useCommander} from "@/plugins/Command";
import {getSelected, SELECTED_STATE} from "@/behavior/click-select";
import clone from '@antv/util/lib/clone'

export const VueFlowEditorProvider = '@@VueFlowEditorProvider'

/**
 * 编辑器各个节点整体样式
 * @author  韦胜健
 * @date    2020/4/29 15:32
 */
export function useEditorStyles(props: { height: number | string | undefined, menuWidth: number | string | undefined, toolbarHeight: number | string | undefined }) {
    return computed(() => ({
        root: {
            height: suffixSize(props.height),
        },
        left: {
            width: suffixSize(props.menuWidth),
        },
        toolbar: {
            height: suffixSize(props.toolbarHeight),
        },
    }))
}

/**
 * 配置G6的插件信息，并且根据props中的属性动态 加载/卸载 插件
 * @author  韦胜健
 * @date    2020/4/29 11:44
 */
export function useEditorPlugins(props: { grid: any, miniMap: any }, graph: any) {
    const pluginState = reactive({
        plugin: {
            grid: null as any,                                                  // 当前网格插件加载的配置信息对象
            miniMap: null as any,                                               // 当前缩略图插件加载的配置信息对象
        },

    })

    watch(
        () => props.grid,
        (val) => {
            if (val === false || val == null) {
                if (!!pluginState.plugin.grid) {
                    graph.removePlugin(pluginState.plugin.grid)
                    pluginState.plugin.grid = null
                }
            } else {
                const pluginConfig = typeof val === "boolean" ? {} : val
                pluginState.plugin.grid = new Grid(pluginConfig)
                graph.addPlugin(pluginState.plugin.grid)
            }
        }
    )

    watch(
        () => props.miniMap,
        (val) => {
            if (val === false || val == null) {
                if (!!pluginState.plugin.miniMap) {
                    graph.removePlugin(pluginState.plugin.miniMap)
                    pluginState.plugin.miniMap = null
                }
            } else {
                const pluginConfig = typeof val === "boolean" ? {} : val
                pluginState.plugin.miniMap = new Minimap(pluginConfig)
                graph.addPlugin(pluginState.plugin.miniMap)
            }
        }
    )

    return {
        pluginState
    }
}

/**
 * 对canvas组件所需要的属性做响应式缓存
 * @author  韦胜健
 * @date    2020/4/29 16:46
 */
export function useCanvasProps(props: { data?: object, grid: boolean, miniMap: boolean }) {

    const canvasProps = reactive({
        data: props.data,
        grid: props.grid,
        miniMap: props.miniMap,
    })

    watch(() => props.data, val => canvasProps.data = val)
    watch(
        () => props.grid,
        val => canvasProps.grid = val
    )
    watch(
        () => props.miniMap,
        val => canvasProps.miniMap = val
    )

    return canvasProps
}


/**
 * 注册编辑器常用的命令，以便撤销以及重做命令
 * @author  韦胜健
 * @date    2020/4/30 15:19
 */
export function useEditorCommander(editorState: any) {
    const commander = useCommander(editorState)

    /*网格切换*/
    commander.register(new Command({
        name: 'switchGrid',
        execute() {
            const grid = editorState.canvasProps.grid
            return {
                redo: () => {
                    editorState.canvasProps.grid = !grid
                },
                undo: () => {
                    editorState.canvasProps.grid = grid
                },
            }
        },
    }))

    /*缩略图切换*/
    commander.register(new Command({
        name: 'switchMiniMap',
        execute() {
            const miniMap = editorState.canvasProps.miniMap
            return {
                redo: () => {
                    editorState.canvasProps.miniMap = !miniMap
                },
                undo: () => {
                    editorState.canvasProps.miniMap = miniMap
                },
            }
        },
    }))

    /*适应画布切换*/
    commander.register(new Command({
        name: 'fitView',
        execute() {

            const graph = this.graph
            const zoom = graph.getZoom()

            return {
                redo: () => {
                    graph.fitView(20)
                },
                undo: () => {
                    graph.zoomTo(zoom)
                },
            }
        }
    }))

    /*实际尺寸切换*/
    commander.register(new Command({
        name: 'actualView',
        execute() {
            const graph = this.graph
            const zoom = graph.getZoom()
            return {
                redo: () => {
                    graph.zoomTo(1)
                },
                undo: () => {
                    graph.zoomTo(zoom)
                },
            }
        }
    }))

    /*放大*/
    commander.register(new Command({
        name: 'zoomIn',
        keyboard: 'ctrl+=',
        execute() {
            const graph = this.graph
            let zoom = graph.getZoom();

            return {
                redo: () => {
                    graph.zoom(1.1)
                },
                undo: () => {
                    graph.zoomTo(zoom)
                }
            }
        }
    }))

    /*缩小*/
    commander.register(new Command({
        name: 'zoomOut',
        keyboard: 'ctrl+-',
        execute() {
            const graph = this.graph
            let zoom = graph.getZoom();

            return {
                redo: () => {
                    graph.zoom(0.9)
                },
                undo: () => {
                    graph.zoomTo(zoom)
                }
            }
        }
    }))

    /*删除节点*/
    commander.register(new Command({
        name: 'delete',
        keyboard: [
            'ctrl+d',
            'backspace',
            'delete',
        ],
        init() {
            this.data = {
                onSelectChange: (selected) => {
                    this.data = {
                        ...this.data,
                        selected,
                    }
                },
            }
            this.graph.on('select-change', this.data.onSelectChange)
        },
        destroy() {
            this.graph.off('select-change', this.data.onSelectChange)
        },
        execute() {

            const graph = this.graph

            let before;
            let after;

            return {
                redo: async () => {
                    if (!before && !after) {
                        const {beforeDelete, afterDelete} = editorState.props

                        before = clone(graph.save())
                        await Promise.all(flatten(Object.values(getSelected(graph))).map(async (i) => {
                            const model = i.get('model')
                            const type = i.get('type')
                            try {
                                if (!!beforeDelete) {
                                    await beforeDelete(model, type)
                                }
                                i.clearAnchor()
                                graph.removeItem(i)
                                graph.paint()
                                if (!!afterDelete) {
                                    await afterDelete(model, type)
                                }
                            } catch (e) {
                                console.error(e)
                            }
                        }))
                        after = clone(graph.save())
                    } else {
                        graph.read(after)
                    }
                },
                undo: () => {
                    graph.read(before)
                }
            }
        },
        isEnable() {
            let {selected} = this.data
            if (!selected) {
                return false
            } else {
                const {nodes, edges} = selected
                return [...nodes, ...edges].length > 0
            }
        }
    }))

    /*拖拽节点*/
    commander.register(new Command({
        name: 'drag',
        doNothingWhenExecute: true,
        init() {
            this.data = {
                ondragstart: () => {
                    this.data = {
                        ...this.data,
                        start: clone(this.graph.save())
                    }
                },
                ondragend: () => {
                    this.data = {
                        ...this.data,
                        end: clone(this.graph.save())
                    }

                    commander.commands.drag()
                },
            }
            this.graph.on('drag-node:start', this.data.ondragstart)
            this.graph.on('drag-node:end', this.data.ondragend)
        },
        destroy() {
            this.graph.off('drag-node:start', this.data.ondragstart)
            this.graph.off('drag-node:end', this.data.ondragend)
        },
        execute() {
            const {start, end} = this.data
            return {
                redo: () => {
                    this.graph.read(end)
                },
                undo: () => {
                    this.graph.read(start)
                },
            }
        },
    }))

    /*全选*/
    commander.register(new Command({
        name: 'selectAll',
        keyboard: 'ctrl+a',
        isQueue: false,
        execute() {
            return {
                redo: () => {
                    if (editorState.props.multipleSelect) {
                        const selected = {
                            nodes: this.graph.findAll('node', () => true),
                            edges: this.graph.findAll('edge', () => true),
                        };
                        flatten(Object.values(selected)).forEach(item => {
                            item.setState(SELECTED_STATE, true)
                        })
                        this.graph.paint()
                        this.graph.emit('select-change', selected)
                    }
                }
            }
        }
    }))

    /*添加连接线*/
    commander.register(new Command({
        name: 'addEdge',
        doNothingWhenExecute: true,
        init() {
            this.data = {
                beforeAdd: () => {
                    this.data = {
                        ...this.data,
                        before: clone(this.graph.save())
                    }
                },
                afterAdd: () => {
                    this.data = {
                        ...this.data,
                        after: clone(this.graph.save())
                    }
                    commander.commands.addEdge()
                }
            }
            this.graph.on('add-edge:before', this.data.beforeAdd)
            this.graph.on('add-edge:after', this.data.afterAdd)
        },
        destroy() {
            this.graph.off('add-edge:before', this.data.beforeAdd)
            this.graph.off('add-edge:after', this.data.afterAdd)
        },
        execute() {
            const {before, after} = this.data
            return {
                redo: () => {
                    this.graph.read(after)
                },
                undo: () => {
                    this.graph.read(before)
                }
            }
        }
    }))

    /**
     * 添加节点
     * @author  韦胜健
     * @date    2020/5/9 9:25
     */
    commander.register(new Command({
        name: 'addNode',
        doNothingWhenExecute: true,
        init() {
            this.data = {
                beforeAdd: () => {
                    this.data = {
                        ...this.data,
                        before: clone(this.graph.save())
                    }
                },
                afterAdd: () => {
                    this.data = {
                        ...this.data,
                        after: clone(this.graph.save())
                    }
                    commander.commands.addNode()
                }
            }
            this.graph.on('add-node:before', this.data.beforeAdd)
            this.graph.on('add-node:after', this.data.afterAdd)
        },
        destroy() {
            this.graph.off('add-node:before', this.data.beforeAdd)
            this.graph.off('add-node:after', this.data.afterAdd)
        },
        execute() {
            const {before, after} = this.data
            return {
                redo: () => {
                    this.graph.read(after)
                },
                undo: () => {
                    this.graph.read(before)
                }
            }
        }
    }))

    /**
     * 更新节点
     * @author  韦胜健
     * @date    2020/5/9 9:25
     */
    commander.register(new Command({
        name: 'update',
        doNothingWhenExecute: true,
        init() {
            this.data = {
                before: null,
                after: null,
            }
        },
        execute(model) {

            const graph = this.graph
            const item = graph.findById(model.id);

            this.data.before = clone(graph.save())
            if (!!item) {
                graph.update(item, model);
                graph.paint()
                this.data.after = clone(graph.save())
            } else {
                this.data.after = this.data.before
            }

            const {before, after} = this.data

            return {
                redo: () => {
                    graph.read(after)
                },
                undo: () => {
                    graph.read(before)
                },
            }
        },
    }))

    return commander
}